#include <stdlib.h>
#include "Mailer.h"
#include  "Shell.h"
extern "C" {
void *mailer_func(void *args) {
	//	cout << "mailer func" << endl;
	Mailer *m = (Mailer *) args;
	m->run();
	return NULL;
}
}

void init(map<int, Node *> * nodes) {
	//run all nodes:

	for (map<int, Node*>::iterator i = nodes->begin(); i != nodes->end(); i++) {
		if (i->second) {
			//			cout << "starting node " << i->second->getID() << endl;
			i->second->start();

		}

	}

}

Mailer::Mailer(map<int, Node *> * data, int size, Shell * shell) :
	bufferSize(size) {
	this->nodes = data;
	this->shell = shell;
	this->stabilaize = true; //sys is stable before running
	this->active = true;
	this->contextSwitches = 0;
	this->quantaUsage = 0;
	this->schedule = 0;
	this->algQuanta = 0;
	cout << "limit of mailer buffer:" << size << endl;

	this->setLimit(size);
}

Mailer::~Mailer() {
	cout << "mailer des'tor" << endl;
//	for (map<int, Node*>::iterator i = nodes->begin(); i != nodes->end(); i++) {
//		if (i->second)
//			delete i->second;
//	}
}

void Mailer::start() {

	// Initialize the thread itself
	//if (pthread_create(&thread, NULL, mailer_func, this) != 0)
	pthread_t mailer_id;
	cout << "creating mailer -only once!" << endl;
	if (pthread_create(&mailer_id, NULL, mailer_func, this) != 0) {
		perror("Mailer:OSThread thread creation failed");
		exit(1);
	}
}

pthread_t Mailer::getThread() {
	return thread;
}
map<int, Node *> * Mailer::getNodes() {
	return nodes;
}
void Mailer::run() {
	cout << "running for my life!" << endl;
	init(nodes);//start the nodes threads
	cout << "done init" << endl;
	map<int, Node*> copy = *nodes;
	while (active) {
		//sleep(1);
		//		cout << "inbox size = " << get_inbox_size() << " ~" << endl;
		if (!stabilaize) {
			//			cout<<"Mailer RR"<<endl;
			switch (this->schedule) {
			case RR:
				RRschedule();
				break;
			case MLQ:
				MLQSchedule();
				break;
			case OUR:
				OurSchedule();
				break;
			}
			if (!this->get_inbox_size() && !this->get_sys_inbox_size()) {
				bool st = true;
				for (map<int, Node*>::iterator it = nodes->begin(); st && it
						!= nodes->end(); it++)
					if (((Node*) it->second)->get_inbox_size()
							|| ((Node*) it->second)->get_sys_inbox_size()) {
						st = false;
						/*					cout << "node " << it->first << " inbox = "
						 << ((Node*) it->second)->get_inbox_size()
						 << " sys_inbox : "
						 << ((Node*) it->second)->get_sys_inbox_size()
						 << endl;*/
					}

				if (st) {
					stringstream s;
					s << "Mailer Statistics: " << endl;
					s << "# of contact switches: " << this->contextSwitches
							<< endl;
					s << "# of quanta used : " << this->quantaUsage << endl;
					s << "========================" << endl;
					shell->insertMsg(
							new Message(s.str(), 0, 0, Message::SYSTEM));
					this->contextSwitches = 0;
					this->quantaUsage = 0;
				}
				this->stabilaize = st;
			}
		}
		while (get_sys_inbox_size() || get_inbox_size())
			popMsg();

		//cout << "pop msg(" << get_inbox_size() << ")" << endl;
	}//infi while loop end
	cout << "Mailer dead" << endl;
}

void Mailer::RRschedule() {
	for (int i = 1; i <= nodes->size(); i++) {
		if ((*nodes)[i] && (*nodes)[i]->active && !(*nodes)[i]->blocked) {
			//									cout<<"setting node "<<i<<" quanta"<<endl;
			//			bool lastYield = (*nodes)[i]->yielded;
			//			bool lastBloked = (*nodes)[i]->blocked;
			(*nodes)[i]->studentSetQuanta(algQuanta);

			//			while ((*nodes)[i]->getQuanta() != 0 && (*nodes)[i]->yielded == false && get_inbox_size()!=limit) {
			while ((*nodes)[i]->getQuanta() != 0 && !(*nodes)[i]->yielded
					&& !(*nodes)[i]->blocked) {
				//sleep(1);
				/*	cout << "Mailer: node " << i << " quanta "
				 << (*nodes)[i]->getQuanta() << " yield "
				 << (*nodes)[i]->yielded << endl;*/
			}
			//context switch
			this->contextSwitches++;
			calcQuantaUsage(algQuanta,(*nodes)[i]->getQuanta());

			//			cout<<"ended"<<endl;
		}//(*nodes)

	}

}

void Mailer::OurSchedule() {

	for (int i = 1; i <= nodes->size(); i++) {
		if ((*nodes)[i] && (*nodes)[i]->active) {
			int realQuanta = ((*nodes)[i]->getQuanta()?algQuanta:algQuanta*2);
			(*nodes)[i]->studentSetQuanta(realQuanta);
//			if ((*nodes)[i]->getQuanta() != 0)
//				(*nodes)[i]->studentSetQuanta(algQuanta);
//			else
//				(*nodes)[i]->studentSetQuanta(algQuanta * 2);

			while ((*nodes)[i]->getQuanta() != 0 && !(*nodes)[i]->yielded
					&& !(*nodes)[i]->blocked) {
			}
			calcQuantaUsage(realQuanta,(*nodes)[i]->getQuanta());
			//context switch
			this->contextSwitches++;

		}//(*nodes)

	}
}

void Mailer::MLQSchedule() {
	for (int priority = 4; priority > -1; priority--)
		for (int i = 1; i <= nodes->size(); i++) {
			if ((*nodes)[i]->priority == priority && (*nodes)[i]
					&& (*nodes)[i]->active && !(*nodes)[i]->blocked) {
				//									cout<<"setting node "<<i<<" quanta"<<endl;
				(*nodes)[i]->studentSetQuanta(algQuanta);

				//			while ((*nodes)[i]->getQuanta() != 0 && (*nodes)[i]->yielded == false && get_inbox_size()!=limit) {
				while ((*nodes)[i]->getQuanta() != 0 && !(*nodes)[i]->yielded
						&& !(*nodes)[i]->blocked) {
					//sleep(1);
					/*	cout << "Mailer: node " << i << " quanta "
					 << (*nodes)[i]->getQuanta() << " yield "
					 << (*nodes)[i]->yielded << endl;*/
				}
				//context switch
				this->contextSwitches++;
				calcQuantaUsage(algQuanta,(*nodes)[i]->getQuanta());
				if ((*nodes)[i]->getQuanta() == 0) {
					(*nodes)[i]->priority = ((*nodes)[i]->priority + 1) % 4;
				} else if ((*nodes)[i]->getQuanta() >= algQuanta - 1
						&& (*nodes)[i]->getQuanta() < (algQuanta / 2)) {
					int newP = ((*nodes)[i]->priority - 1);
					(*nodes)[i]->priority = (newP ? newP : 0);
				}//if not either : same priority;


				//			cout<<"ended"<<endl;
			}//(*nodes)

		}
}
void Mailer::handleSysMsg(Message* msg) {
	//	cout << "mailer handle SYS message" << endl;
	//		cout << msg->toString() << endl;
	if (msg->kill || msg->revive) {
		if (msg->revive)
			(*nodes)[msg->reciver]->active = true;
		(*nodes)[msg->reciver]->insertMsg(msg);
		if (msg->revive) {
			(*nodes)[msg->reciver]->stable = false;
			//			cout<<"restarting node!"<<endl;
			(*nodes)[msg->reciver]->start();
			this->stabilaize = false;
		}
	} else {
		shell->insertMsg(msg);
	}

}

void Mailer::calcQuantaUsage(int realQuanta,int nodeQuantaLeft) {
	//	cout << "quantaUsage (befor) : " << quantaUsage <<" used: " << nodeQuantaLeft<< endl;
	this->quantaUsage += realQuanta - (realQuanta - nodeQuantaLeft);
	//	cout << "quantaUsage (after) : " << quantaUsage << endl;
}
void Mailer::handleRTMsg(Message* msg) {
	//	cout<<"mailer handle RT message"<<endl;
	if (nodes->at(msg->reciver)->active) {
		msg->history.push_back(msg->reciver);
		msg->ballCounter++;
		(*nodes)[msg->reciver]->insertMsg(msg);
	} else
		delete msg;
}
void Mailer::handleMsg(Message* msg) {
	//	cout << "mailer handeling regular msg" << endl;
	msg->ballCounter++;
	(*nodes)[msg->sender]->insertMsg(msg);
}

void Mailer::setStabilize(bool stab) {
	stabilaize = stab;
}
bool Mailer::getStabilize() {
	return stabilaize;
}
